1 /* 2 * Hunt - a framework for web and console application based on Collie using Dlang development 3 * 4 * Copyright (C) 2015-2017 Shanghai Putao Technology Co., Ltd 5 * 6 * Developer: HuntLabs 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.i18n; 13 14 import std.path; 15 import std.file; 16 import std.algorithm; 17 import std.array; 18 import kiss.logger; 19 import std.stdio; 20 import std.string; 21 22 alias StrStr = string[string]; 23 alias StrStrStr = string[string][string]; 24 25 enum I18N_DEFAULT_LOCALE = "zh-cn"; 26 27 ///国际化 28 class I18n 29 { 30 private{ 31 StrStrStr _res; 32 __gshared I18n _instance; 33 string _default; 34 } 35 36 this() 37 { 38 _default = I18N_DEFAULT_LOCALE; 39 } 40 41 static I18n instance() 42 { 43 if(_instance is null) 44 { 45 _instance = new I18n(); 46 } 47 return _instance; 48 } 49 50 ///加载资源文件 51 bool loadLangResources(string path, lazy string ext = "res") 52 { 53 _isResLoaded = false; 54 auto resfiles = std.file.dirEntries(path, "*.{res}", SpanMode.depth) 55 .filter!(a => a.isFile) 56 .map!(a => std.path.absolutePath(a.name)) 57 .array; 58 if(resfiles.length == 0) 59 { 60 logDebug("lang res file is empty"); 61 return false; 62 } 63 64 foreach(r; resfiles) 65 { 66 parseResFile(r); 67 } 68 _isResLoaded = true; 69 return true; 70 } 71 72 @property bool isResLoaded() { return _isResLoaded; } 73 private bool _isResLoaded = false; 74 75 @property StrStrStr resources() 76 { 77 return this._res; 78 } 79 80 ///设置默认 81 @property defaultLocale(string loc) 82 { 83 this._default = loc; 84 } 85 86 @property string defaultLocale() 87 { 88 return this._default; 89 } 90 91 ///解析文件 92 private bool parseResFile(string fileName) 93 { 94 auto f = File(fileName,"r"); 95 scope(exit) 96 { 97 f.close(); 98 } 99 100 if(!f.isOpen()) return false; 101 102 string _res_file_name = baseName(fileName, extension(fileName)); 103 string _loc = baseName(dirName(fileName)); 104 105 int line = 1; 106 while(!f.eof()) 107 { 108 scope(exit) 109 line += 1; 110 string str = f.readln(); 111 str = strip(str); 112 if(str.length == 0) 113 continue; 114 if(str[0] == '#' || str[0] == ';') 115 continue; 116 auto len = str.length -1; 117 118 auto site = str.indexOf("="); 119 if(site == -1) 120 { 121 import std.format; 122 throw new Exception(format("the format is erro in file %s, in line %d : string: %s",fileName,line, str)); 123 } 124 string key = str[0..site].strip; 125 if(key.length == 0) 126 { 127 import std.format; 128 throw new Exception(format("the Key is empty in file %s, in line %d",fileName,line)); 129 } 130 string value = str[site + 1..$].strip; 131 132 this._res[_loc][_res_file_name ~ "." ~ key] = value; 133 } 134 return true; 135 } 136 137 } 138 139 ///设置本地化 140 private string _local = I18N_DEFAULT_LOCALE; 141 142 @property string getLocale(){ 143 if(_local) 144 return _local; 145 return I18n.instance().defaultLocale; 146 } 147 148 ///设置本地化 149 @property setLocale(string _l) 150 { 151 _local = toLower(_l); 152 } 153 154 ///key is [filename.key] 155 string getText(string key, lazy string default_value = string.init) 156 { 157 I18n i18n = I18n.instance(); 158 if(!i18n.isResLoaded) 159 { 160 logWarning("The lang resources has't loaded yet!"); 161 return key; 162 } 163 164 auto p = getLocale in i18n.resources; 165 if(p !is null) 166 { 167 return p.get(key, default_value); 168 } 169 logDebug("unsupported local: ", getLocale, ", use default now: ", i18n.defaultLocale); 170 171 p = i18n.defaultLocale in i18n.resources; 172 173 if(p !is null) 174 { 175 return p.get(key, default_value); 176 } 177 178 logDebug("unsupport local ", i18n.defaultLocale ); 179 180 return default_value; 181 } 182 183 184 185 /* 186 unittest{ 187 188 I18n i18n = I18n.instance(); 189 i18n.loadLangResources("./resources/lang"); 190 i18n.defaultLocale = "en-us"; 191 writeln(i18n.resources); 192 193 194 /// 195 setLocale("en-br"); 196 assert( getText("message.hello-world", "empty") == "Hello, world"); 197 198 /// 199 setLocale("zh-cn"); 200 assert( getText("email.subject", "empty") == "收件人"); 201 202 203 setLocale("en-us"); 204 assert( getText("email.subject", "empty") == "empty"); 205 } 206 */